/* confest.c - confidence estimator routines */


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>

#include "host.h"
#include "misc.h"
#include "machine.h"
#include "confest.h"

/* print estimator stats */
void
confest_stats(struct confest_t *estimator,	/* branch predictor instance */
	      FILE *stream)			/* output stream */
{
 
    // High confidence stats
    fprintf(stream, "conf: high confidence hits = %f\n",(double)estimator->high_hits);
    
    fprintf(stream, "conf: high confidence misses = %f\n",(double)estimator->high_misses);
    
    fprintf(stream, "conf: high confidence hit rate = %f\n",(double)estimator->high_hits/(double)(estimator->high_hits+estimator->high_misses));
    
    // Low confidence stats
    fprintf(stream, "conf: low confidence hits = %f\n",(double)estimator->low_hits);
    
    fprintf(stream, "conf: low confidence misses = %f\n",(double)estimator->low_misses);   
    
    fprintf(stream, "conf: low confidence hit rate = %f\n",(double)estimator->low_hits/(double)(estimator->low_hits+estimator->low_misses));
  
}

/* register confidence esitmator stats */
void
confest_reg_stats(struct confest_t *estimator,	/* branch predictor instance */
		  struct stat_sdb_t *sdb)		/* stats database */
{
  char buf[512], buf1[512], *name;
  
  name="conf_est";
  
  sprintf(buf, "%s.lookups", name);
  stat_reg_counter(sdb, buf, "total number of confidence estimator lookups",
		   &estimator->lookups, 0, NULL);
  
  // High confidence stats
  sprintf(buf, "%s.high_total", name);
  sprintf(buf1, "%s.high_hits + %s.high_misses", name, name);
  stat_reg_formula(sdb, buf, "total number of high confidence predictions", buf1, "%12.0f");
  
   sprintf(buf, "%s.high_percent", name);
  sprintf(buf1, "%s.high_total / (%s.lookups)", name, name);
  stat_reg_formula(sdb, buf,
		  "high confidence percentage (i.e., high-total/branch_total)",
		  buf1, "%9.4f");
  
  sprintf(buf, "%s.high_hits", name);
  stat_reg_counter(sdb, buf, "total number of high-confidence hits", 
		   &estimator->high_hits, 0, NULL);
  
  sprintf(buf, "%s.high_misses", name);
  stat_reg_counter(sdb, buf, "total number of high-confidence misses", 
		   &estimator->high_misses, 0, NULL);
  
  sprintf(buf, "%s.high_hit_rate", name);
  sprintf(buf1, "%s.high_hits / (%s.high_total)", name, name);
  stat_reg_formula(sdb, buf,
		  "high confidence hit rate (i.e., high-hits/high-total)",
		  buf1, "%9.4f");
  
  
  // Low confidence stats
  sprintf(buf, "%s.low_total", name);
  sprintf(buf1, "%s.low_hits + %s.low_misses", name, name);
  stat_reg_formula(sdb, buf, "total number of low confidence predictions", buf1, "%12.0f");
  
  sprintf(buf, "%s.low_percent", name);
  sprintf(buf1, "%s.low_total / (%s.lookups)", name, name);
  stat_reg_formula(sdb, buf,
		  "low confidence percentage (i.e., low-total/branch_total)",
		  buf1, "%9.4f");
  
  sprintf(buf, "%s.low_hits", name);
  stat_reg_counter(sdb, buf, 
		   "total number of low-confidence hits", 
		   &estimator->low_hits, 0, NULL);
  
  sprintf(buf, "%s.low_misses", name);
  stat_reg_counter(sdb, buf, 
		   "total number of low-confidence misses", 
		   &estimator->low_misses, 0, NULL);
  
  sprintf(buf, "%s.low_hit_rate", name);
  sprintf(buf1, "%s.low_hits / (%s.low_total)", name, name);
  stat_reg_formula(sdb, buf,
		  "low confidence hit rate (i.e., low-hits/low-total)",
		  buf1, "%9.4f");
  
  
}

/* create a confidence estimator */
struct confest_t *			/* branch predictory instance */
confest_create(unsigned int tsize,	/* table size */
	       unsigned int bhrsize,	/* BHR table size */
	       unsigned int shift_width,/* history register width */
	       unsigned int xor)  	/* history xor address flag */
{
  
  struct confest_t *estimator;
  struct confest_ct_t *confest_ct;
  unsigned int cnt;
  
  estimator = calloc(1, sizeof(struct confest_t));
  confest_ct = calloc(1, sizeof(struct confest_ct_t));

  //if (!(estimator = calloc(1, sizeof(struct confest_t))))
   // fatal("out of virtual memory");
  
  confest_ct->config.tsize = tsize;
  
  confest_ct->config.bhrsize = bhrsize;
  
  confest_ct->config.shift_width = shift_width;
  
  confest_ct->config.xor = xor;
  
  //confest_ct->config.shiftregs = calloc(bhrsize, sizeof(int));  
  
  confest_ct->config.table = calloc(tsize, sizeof(int)); 
  
  /* initialize CT table to low confidence*/
  for (cnt = 0; cnt < tsize; cnt++){
    
      confest_ct->config.table[cnt] = 0;
  }
  
  estimator->ct = confest_ct;

  return estimator;
}


int						/* predicted branch confidence(HIGH/LOW) */
confest_lookup(struct confest_t *estimator,	/* branch predictor instance */
	       md_addr_t baddr,			/* branch address */
	       int *shiftregs,		        /* level-1 history table */
	       enum md_opcode op)		/* opcode of instruction */
{
  int confidence = 0;
  struct confest_ct_t *confest_ct;

  // if this is not a branch, return
  if (!(MD_OP_FLAGS(op) & F_CTRL))
    return 0;

  estimator->lookups++;
  
  // calculate index using: PC(baddr) XOR Global BHR
  int bhr_index = (baddr >> MD_BR_SHIFT) & (estimator->ct->config.bhrsize - 1);

  int ct_index = shiftregs[bhr_index];

  if (estimator->ct->config.xor){
 
    #if 1
     // This L2 index computation is more "compatible" to McFarling's
     // verison of it, i.e., if the PC xor address component is only
     // part of the index, take the lower order address bits for the
     // other part of the index, rather than the higher order ones 
 
      ct_index = (((ct_index ^ (baddr >> MD_BR_SHIFT)) & ((1 << estimator->ct->config.shift_width) - 1)) | ((baddr >> MD_BR_SHIFT) << estimator->ct->config.shift_width));
 
    #else
  
      ct_index = ct_index ^ (baddr >> MD_BR_SHIFT);
  
    #endif
  }else{

    ct_index = ct_index | ((baddr >> MD_BR_SHIFT) << estimator->ct->config.shift_width);
  }

  ct_index = ct_index & (estimator->ct->config.tsize - 1);

  // Get a pointer to confidence information 
  int ct_value = 0;
  confest_ct = estimator->ct;
  ct_value =confest_ct->config.table[ct_index];
  
  //printf("ct_value: %d \n", ct_value);
   
  // Use reduction function to estimate confidence
  if(ct_value < 1){
    confidence = 0;//Low
  }
  else{
    confidence = 1;//High 
  }

  return confidence;
}


void
confest_update(struct confest_t *estimator,	/* confidence estimator instance */
	     md_addr_t baddr,			/* branch address */
	     int pred_conf,			/* predicted confidence (0-LOW/1-HIGH) */
	     int correct,			/* was earlier addr prediction ok? */
	     int *shiftregs,		        /* level-1 history table */
	     enum md_opcode op) 		/* opcode of instruction */
{
  struct confest_ct_t *confest_ct;

  /* don't change confidence estimator state for non-branch instructions */
  if (!(MD_OP_FLAGS(op) & F_CTRL))
    return;

  /* Have a branch here */

  /* Update stats */
  if (correct && pred_conf){
    estimator->high_hits++;
  }
  else if(correct && !pred_conf){
    estimator->low_hits++;
  }
  else if(!correct && pred_conf){
    estimator->high_misses++;
  }
  else{
    estimator->low_misses++; 
  }
  
   //calculate index using: PC(baddr) XOR Global BHR
  int bhr_index = (baddr >> MD_BR_SHIFT) & (estimator->ct->config.bhrsize - 1);

  int ct_index = shiftregs[bhr_index];

  if (estimator->ct->config.xor){
 
    #if 1
    /* this L2 index computation is more "compatible" to McFarling's
      verison of it, i.e., if the PC xor address component is only
      part of the index, take the lower order address bits for the
      other part of the index, rather than the higher order ones */
 
      ct_index = (((ct_index ^ (baddr >> MD_BR_SHIFT)) & ((1 << estimator->ct->config.shift_width) - 1)) | ((baddr >> MD_BR_SHIFT) << estimator->ct->config.shift_width));
 
    #else
  
      ct_index = ct_index ^ (baddr >> MD_BR_SHIFT);
  
    #endif
  }else{

    ct_index = ct_index | ((baddr >> MD_BR_SHIFT) << estimator->ct->config.shift_width);
  }

  ct_index = ct_index & (estimator->ct->config.tsize - 1);

  /* get a pointer to confidence information */  
  int ct_value = 0;
  confest_ct = estimator->ct;
  ct_value = confest_ct->config.table[ct_index];  
  
  
  /* Update CT */
  if(correct){
    
    //check if the table entry is already saturated(16), if not saturated increment by one
    if(ct_value < 16){
      confest_ct->config.table[ct_index]++;  
    }
    
  }
  else{
    
    //reset the table entry to 0
    confest_ct->config.table[ct_index] = 0;  
    
    //if not 0 decrement by one
    /*if(ct_value > 0){
      confest_ct->config.table[ct_index]--;
    }*/
    
  }
}
